#include "sphere.h"

sphere::sphere(): primitive()
{
	
}
sphere::sphere(point cen, double rad, const char* _name): primitive(_name)
{
	center = cen;
	radius = rad;
}
sphere::sphere(point cen, double rad): primitive()
{
	center = cen;
	radius = rad;
}
sphere::sphere(point cen): primitive()
{
	center = cen;
	radius = 0.0;	
}
sphere::sphere(point cen, point surface, const char* _name): primitive(_name)
{
	center = cen;
	vector3 temp(cen, surface);
	radius = temp.width();
}
sphere::sphere(point cen, point sufrace): primitive()
{
	center = cen;
	vector3 temp(cen, sufrace);
	radius = temp.width();	
}
sphere::~sphere()
{
	
}
void sphere::getSphereFromXML(std::ifstream &file)
{
	char line[MAX_LENGTH];
	file.getline(line, MAX_LENGTH);
	std::string temp(line);
	std::string nazwaWezla;

	unsigned int i(0);


	while (nazwaWezla!="/sphere")
	{
		file.getline(line, MAX_LENGTH);
		temp.clear();
		temp.append(line);
		nazwaWezla.clear();

		i=0;
		while(temp[i] == 9 || temp[i] == 32)
			++i;
		if(temp[i] == '<')
		{
			std::string nazwaWezlaKoniec;
			std::string zawartoscWezla;
			++i;
			while(temp[i] != '>')
			{
				nazwaWezla.push_back(temp[i]);
				++i;
			}
			if(nazwaWezla == "surfacematerial")
			{
				nazwaWezla.clear();	
				file.getline(line, MAX_LENGTH);
				std::string temp(line);
				i = 0;
				zawartoscWezla.clear();
				while(temp[i] == 9 || temp[i] == 32)
					++i;
				++i;
				while(temp[i] != ' ')
				{
					zawartoscWezla.push_back(temp[i]);
					++i;
				}
				if (zawartoscWezla == "material")
				{
					zawartoscWezla.clear();
					while(temp[i] == 32)
						++i;
					while(temp[i] != '=')
					{
						zawartoscWezla.push_back(temp[i]);
						++i;
					}
					if (zawartoscWezla == "type")
					{
						i+=2;
						zawartoscWezla.clear();
						while(temp[i] != 34)
						{
							zawartoscWezla.push_back(temp[i]);
							++i;
						}
						if (zawartoscWezla == "none")
						{
							matNone tempMat;
							tempMat.getMaterialFromXML(file);
							setMaterial(new matNone(tempMat));
						}
						else if (zawartoscWezla == "lambert")
						{
							matLambert tempMat;
							tempMat.getMaterialFromXML(file);
							setMaterial(new matLambert(tempMat));
						}
						else if (zawartoscWezla == "phong")
						{
							matPhong tempMat;
							tempMat.getMaterialFromXML(file);
							setMaterial(new matPhong(tempMat));
						}
						else if (zawartoscWezla == "blinn")
						{
							matBlinn tempMat;
							tempMat.getMaterialFromXML(file);
							setMaterial(new matBlinn(tempMat));
						}
						else if (zawartoscWezla == "orenNayer")
						{
							matOrenNayer tempMat;
							tempMat.getMaterialFromXML(file);
							setMaterial(new matOrenNayer(tempMat));
						}
						else if (zawartoscWezla == "mirror")
						{
							matMirror tempMat;
							tempMat.getMaterialFromXML(file);
							setMaterial(new matMirror(tempMat));
						}
						else if (zawartoscWezla == "glass")
						{
							matGlass tempMat;
							tempMat.getMaterialFromXML(file);
							setMaterial(new matGlass(tempMat));
						}
					}
				}
			}
			else if(nazwaWezla == "name")
			{
				++i;
				while(temp[i] != '<')
				{
					zawartoscWezla.push_back(temp[i]);
					++i;
				}
				++i;
				while(temp[i] != '>')
				{
					nazwaWezlaKoniec.push_back(temp[i]);
					++i;
				}
				if(nazwaWezlaKoniec == "/name")
					this->name.append(zawartoscWezla.c_str());
				continue;
			}
			else if(nazwaWezla == "name /")
			{
				continue;
			}
			else if(nazwaWezla == "position")
			{
				center.getPointFromXML(file);
				continue;
			}
			else if(nazwaWezla == "radius")
			{
				++i;
				while(temp[i] != '<')
				{
					zawartoscWezla.push_back(temp[i]);
					++i;
				}
				++i;
				while(temp[i] != '>')
				{
					nazwaWezlaKoniec.push_back(temp[i]);
					++i;
				}
				if(nazwaWezlaKoniec == "/radius")
					this->radius = atof(zawartoscWezla.c_str());
				continue;
			}
		}
	}
}
void sphere::setCenter(point cen)
{
	center = cen;
}
void sphere::setRadius(double rad)
{
	radius = rad;
}
void sphere::setCenterRadius(point cen, double rad)
{
	center = cen;
	radius = rad;	
}
void sphere::copySphere(sphere TheOneToCopyFrom)
{
	center = TheOneToCopyFrom.center;
	radius = TheOneToCopyFrom.radius;
}
point sphere::getCenter()
{
	return center;
}
double sphere::getRadius()
{
	return radius;
}
double sphere::getSurfaceArea()
{
	return (double)4 * (double)M_PI * radius * radius;
}
double sphere::getVolume()
{
	return (double)4 / (double)3 * (double)M_PI *  radius * radius * radius;
}
double sphere::maxX()
{
	return center.x + radius;
}
double sphere::minX()
{
	return center.x - radius;
}
double sphere::maxY()
{
	return center.y + radius;
}
double sphere::minY()
{
	return center.y - radius;
}
double sphere::maxZ()
{
	return center.z + radius;
}
double sphere::minZ()
{
	return center.z - radius;
}
int sphere::intersect( ray& Ray, intersection& inter)
{
	vector3 v;
	double distance;
	double b;
	double a ;
	double det;
	int intersectionsCount(0);

	if(Ray.getInsideness())
	{
		v=(Ray.getOrigin() - center);
		distance = v.width();
		b = -v.dot(Ray.getDirection());
		a = Ray.getDirection().dot(Ray.getDirection());
		det = (b * b) - a*(v.dot(v) - (radius * radius));
		
		if (double(det*4.0) > 0.0)
		{
			det = (double)sqrt( det );
			double intersection1 = (b - det)/a;
			double intersection2 = (b + det)/a;
			//if (distance>radius)
			//	inter.distance = intersection2;
			//else
			//	inter.distance = intersection1;
			inter.distance = intersection2;
			intersectionsCount = 1;			
		}
	}
	else
	{
		v=(Ray.getOrigin() - center);
		distance = v.width();
		b = -v.dot(Ray.getDirection());
		a = Ray.getDirection().dot(Ray.getDirection());
		det = (b * b) - a*(v.dot(v) - (radius * radius));
		
		if (double(det*4.0) > 0.0)
		{
			det = (double)sqrt( det );
			double intersection1 = (b - det)/a;
			double intersection2 = (b + det)/a;
			if (intersection2 > 0)
			{
				if (intersection1 < 0)
				{
					if (intersection2 < distance)
					{
						inter.distance = intersection2;
						intersectionsCount = 1;
					}
				}
				else
				{
					if (intersection1 < distance)
					{
						inter.distance = intersection1;
						intersectionsCount = 2;
					}
				}
			}
		}
	}
	inter.objectID = ObjectID;
	vector3 temporaryHelper = Ray.getDirection()*inter.distance;
	inter.colisionCoords = temporaryHelper.movePoint(Ray.getOrigin());
	if(surfaceMaterial->diffuseMapOrColor)
		getTextureCoords(inter);

	return intersectionsCount;
}
bool sphere::isInShade( ray& Ray)
{
	vector3 v;
	v=(Ray.getOrigin() - center);
	double distance = v.width();
	double b = -v.dot(Ray.getDirection());
	double a = Ray.getDirection().dot(Ray.getDirection());
	double det = (b * b) - a*(v.dot(v) - (radius * radius));
	int intersectionsCount(0);
	if (double(det*4.0) > 0.0)
	{
		det = (double)sqrt( det );
		double intersection1 = (b - det)/a;
		double intersection2 = (b + det)/a;
		if (intersection2 > 0)
		{
			if (intersection1 < 0)
			{
				if (intersection2 < distance)
				{
					distance = intersection2;
					intersectionsCount = 1;
				}
			}
			else
			{
				if (intersection1 < distance)
				{
					distance = intersection1;
					intersectionsCount = 2;
				}
			}
		}
	}
	if (intersectionsCount>0)
	{
		vector3 lightDistanceHelper(Ray.getOrigin(), Ray.getDesrination());
		double lightDistance = lightDistanceHelper.width();
		if(lightDistance <= distance)
			return false;
		else
			return true;
	}
	else 
		return false;
}
vector3 sphere::getNormal(point inPoint)
{
	vector3 temp(center,inPoint);
	temp.normalize();
	return temp;
}
void sphere::getTextureCoords(intersection& inter)
{
	vector3 znormalizowany(inter.colisionCoords);
	znormalizowany.normalize();
	double theta = acos(znormalizowany.y);
	double phi = atan2(znormalizowany.x, -znormalizowany.z);
	if (phi < 0.0)
		phi += 2.0*M_PI;
	inter.texCoords.setTexCoords((phi/(M_PI*2.0)), (1.0 - theta/(M_PI*1.0)));
}